﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using paravili.Services;
using Sebarf.Diagnostics.Interfaces;

#endregion

namespace Tests.Basics.Services {
	/// <summary>
	/// Description of the class
	/// </summary>
	[TestClass]
	public class TestDoMatchingByAFastSocialHeuristicService {
		#region Test Methods

		// public methods located here

		[TestMethod]
		public void TestOnlyOneValidConnectionBetweenTwoCluster() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();
			/*
			 * Cluster
			 *   -   - 
			 *   2   -
			 * 
			*/
			var input = new[,] { { int.MinValue, int.MinValue }, { 2, int.MinValue } };

			// do work
			Match[] result = testCandidate.FindMatch(input);

			// check
			Assert.AreEqual(result.Length, 1);

			Assert.IsTrue(result[0].FromIndex == 1 && result[0].ToIndex == 0);
		}

		[TestMethod]
		public void TestNoOneValidConnectionBetweenTwoCluster() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();
			/*
			 * Cluster
			 *   -   - 
			 *   -   -
			 * 
			*/
			var input = new[,] { { int.MinValue, int.MinValue }, { int.MinValue, int.MinValue } };

			// do work
			Match[] result = testCandidate.FindMatch(input);

			// check
			Assert.AreEqual(result.Length, 0);
		}

		[TestMethod]
		public void TestTwoValidConnectionDiagonalBetweenTwoCluster() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();
			/*
			 * Cluster
			 *   -   -2 
			 *   -6  -
			 * 
			*/
			var input = new[,] { { int.MinValue, -2 }, { -6, int.MinValue } };

			// do work
			Match[] result = testCandidate.FindMatch(input);

			// check
			Assert.AreEqual(result.Length, 1);

			Assert.IsTrue(result[0].FromIndex == 0 && result[0].ToIndex == 1);
		}

		[TestMethod]
		public void TestConnection3To1Deterministic() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			int[,] input = GenerateEmptyArrayOfSize(4);
			// set connection from c1 to c2 with 70
			SetDistanceFromToCluster(input, 1, 2, 70);
			// set connection from c4 to c2 with 60
			SetDistanceFromToCluster(input, 4, 2, 60);
			// set connection from c3 to c2 with 20
			SetDistanceFromToCluster(input, 3, 2, 20);

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			// check, only connection from c3 to c2 should be returned
			Assert.AreEqual(result.Length, 1);
			Assert.IsTrue(result[0].FromIndex == 2 && result[0].ToIndex == 1);
		}

		[TestMethod]
		public void TestConnectionSelfConnection() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			int[,] input = GenerateEmptyArrayOfSize(2);
			// set connection from c1 to c2 with 70
			SetDistanceFromToCluster(input, 1, 1, 70);

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			// check, only connection from c3 to c2 should be returned
			Assert.AreEqual(result.Length, 0);
		}

		[TestMethod]
		public void TestConnectionSelfConnectionAndAnotherOne() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			int[,] input = GenerateEmptyArrayOfSize(2);
			// set connection from c1 to c2 with 70
			SetDistanceFromToCluster(input, 1, 1, 10);
			SetDistanceFromToCluster(input, 2, 1, 15);
			SetDistanceFromToCluster(input, 1, 2, 25);

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			// check, only connection from c3 to c2 should be returned
			Assert.AreEqual(result.Length, 1);
			Assert.IsTrue(result[0].FromIndex == 1 && result[0].ToIndex == 0);
		}

		[TestMethod]
		public void TestConnection4To1Deterministic() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			int[,] input = GenerateEmptyArrayOfSize(5);
			// set connection from c1 to c2 with 70
			SetDistanceFromToCluster(input, 1, 2, 70);
			// set connection from c4 to c2 with 60
			SetDistanceFromToCluster(input, 4, 2, 60);
			// set connection from c3 to c2 with 20
			SetDistanceFromToCluster(input, 3, 2, 20);
			// set connection from 53 to c2 with 10
			SetDistanceFromToCluster(input, 5, 2, 10);

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			// check, only connection from c3 to c2 should be returned
			Assert.AreEqual(result.Length, 1);
			Assert.IsTrue(result[0].FromIndex == 4 && result[0].ToIndex == 1);
		}

		[TestMethod]
		public void TestConnection4Circle() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			int[,] input = GenerateEmptyArrayOfSize(5);
			// set connection from c1 to c2 with 70
			SetDistanceFromToCluster(input, 1, 2, 70);
			// set connection from c2 to c3 with 60
			SetDistanceFromToCluster(input, 2, 3, 60);
			// set connection from c3 to c4 with 20
			SetDistanceFromToCluster(input, 3, 4, 20);
			// set connection from c4 to c1 with 10
			SetDistanceFromToCluster(input, 4, 1, 10);

			WriteData(input);

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			input = GenerateNullArrayOfSize(4);

			foreach (Match match in result) {
				SetDistanceFromToCluster(input, match.FromIndex + 1, match.ToIndex + 1, -1);
			}
			WriteData(input);

			// check, only connection from c3 to c2 should be returned
			Assert.AreEqual(result.Length, 2);
			Assert.IsTrue(result[1].FromIndex == 3 && result[1].ToIndex == 0);
			Assert.IsTrue(result[0].FromIndex == 1 && result[0].ToIndex == 2);
		}

		[TestMethod]
		public void TestConnectionFullMatrixOfFour() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			var input = new[,] { { 10, 42, 25, 22 }, { 12, 47, 21, 37, }, { 30, 43, 48, 07, }, { 24, 09, 46, 45 }, };

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			// check, only connection from c3 to c2 should be returned
			Assert.AreEqual(result.Length, 2);
			//Assert.IsTrue(result[0].FromIndex == 4 && result[0].ToIndex == 1);
		}

		[TestMethod]
		public void Test4ClustersFullyConnected() {
			var testCandidate = new DoMatchingByAFastSocialHeuristicService();

			int[,] input = GenerateEmptyArrayOfSize(4);
			// randomizly add values
			var r = new Random();
			for (int i = 0; i < input.GetLength(0); i++) {
				for (int y = 0; y < input.GetLength(0); y++) {
					input[i, y] = r.Next(1, 50);
				}
			}
			WriteData(input);

			// do work
			Match[] result = testCandidate.FindMatch(input).OrderBy(x => x.FromIndex).ToArray();

			input = GenerateNullArrayOfSize(4);

			foreach (Match match in result) {
				SetDistanceFromToCluster(input, match.FromIndex + 1, match.ToIndex + 1, -1);
			}
			WriteData(input);

			// check, only two cnn are returned
			Assert.IsTrue(result.Length <= 2);
		}

		private int[,] GenerateNullArrayOfSize(int i) {
			return InitializeArrayWith(i, 0);
		}

		private int[,] InitializeArrayWith(int p, int defaultValue) {
			var toReturn = new int[p, p];
			for (int i = 0; i < p; i++) {
				for (int y = 0; y < p; y++) {
					toReturn[i, y] = defaultValue;
				}
			}
			return toReturn;
		}

		private void WriteData(int[,] input) {
			var builder = new StringBuilder();
			for (int i = 0; i < input.GetLength(0); i++) {
				builder.AppendLine();
				for (int y = 0; y < input.GetLength(1); y++) {
					builder.Append(input[i, y].ToString("00\t"));
				}
			}
			builder.AppendLine();
			Logger.WriteDebug(builder.ToString());
		}

		private static void SetDistanceFromToCluster(int[,] input, int fromCluster, int toCluster, int distance) {
			input[fromCluster - 1, toCluster - 1] = -1 * distance;
		}

		private int[,] GenerateEmptyArrayOfSize(int p) {
			return InitializeArrayWith(p, int.MinValue);
		}

		#endregion
	}
}